// Ŭnicode please
#pragma once

#include <string>
#include <vector>
#include <memory>
#include <map>
#include <mio.h>
#include "SRData.h"
#include "SRObjModel.h"

namespace sora
{
	class ObjNodeState;
	class ObjNodeFSM;

	//parser가 2개씩 있을 필요가 없다
	class ObjModelParser {
	public:
		std::vector<std::string> split(const std::string &line) const;

		static ObjModelParser &parser();
	private:
		ObjModelParser();
		ObjModelParser(const ObjModelParser &other);
		ObjModelParser& operator=(const ObjModelParser &other);
	};

	class ObjModelReader {
	public:
		ObjModelReader(const std::string &file);
		std::string readLine();
		bool canRead();
	private:
		std::auto_ptr<mio::BufferFileReader> reader_;
	};

	class ObjModelLoader {
	public:
		ObjModelLoader();
		//parse
		Model *load(const std::string &file) const;
		void getModelNodeData(const std::vector<std::string> &lines, std::vector<ObjNodeData*> &nodelist) const;

	private:
		mutable std::auto_ptr<ObjNodeFSM> fsm_;
		ObjNodeFSM &fsm() const;
	};

#if _IPHONE_
#pragma mark obj model elem stateObjNodeData
#endif
	class ObjNodeFSM {
	public:
		ObjNodeFSM();
		~ObjNodeFSM();

		ObjNodeState &state(const std::string &keywordStr) const;
		ObjNodeState &state(ObjNodeKeyword keyword) const;

		//data access + set
		void addPos(const matsu::vec4 &pos);
		void addTexCoord(const matsu::vec2 &texCoord);
		void addNormal(const matsu::vec3 &normal);

		const matsu::vec4 &pos(int index) const;
		const matsu::vec2 &texCoord(int index) const;
		const matsu::vec3 &normal(int index) const;

		//fsm state
		ObjNodeState &curr() const;
		void transit(ObjNodeKeyword keyword);

		void flush(MeshModelBuilder &builder);

	private:
		std::map<ObjNodeKeyword, ObjNodeState*> stateMap_;

		//data
		std::vector<matsu::vec4> posList_;
		std::vector<matsu::vec2> texCoordList_;
		std::vector<matsu::vec3> normalList_;

		bool isCurrExist() const;
		ObjNodeState *curr_;
	};

	class ObjNodeState {
	public:
		ObjNodeState(ObjNodeFSM *fsm) : fsm_(fsm) {}
		virtual ~ObjNodeState() {}

		void parse(const std::string &line, ObjNodeData &data);
		virtual void parse(const std::vector<std::string> &tokens, ObjNodeData &data) = 0;

		virtual void update(const ObjNodeData &data, MeshModelBuilder &builder) = 0;
		virtual void flush(MeshModelBuilder &builder) {}

		virtual ObjNodeState *next(ObjNodeKeyword keyword) = 0;
		ObjNodeState *next(const ObjNodeData &data);
	protected:
		ObjNodeFSM &fsm() const;
		ObjNodeFSM *fsm_;
	};
	//group별로 파싱 방법이 거의 동일하고 keyword만 다르게 될듯. 2단계 상속구조로 상태구성
	//elements
	class ObjNodeCategoryElementState : public ObjNodeState {
	public:
		ObjNodeCategoryElementState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);

		void update(const ObjNodeData &data, MeshModelBuilder &builder);
		ObjNodeState *next(ObjNodeKeyword keyword);
	};

	class ObjNodeFaceState : public ObjNodeCategoryElementState {
	public:
		ObjNodeFaceState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);

		void flush(MeshModelBuilder &builder);
	private:
		void checkVertexNormalTexUse(const std::vector<std::string> &tokens, bool &useTex, bool &useNormal);

		bool prevUseTex_;
		bool prevUseNormal_;
		std::vector<ModelVertex3> vertexList_;
		std::vector<GLushort> indexList_;
	};

	//vertex data
	class ObjNodeCategoryVertexDataState : public ObjNodeState {
	public:
		ObjNodeCategoryVertexDataState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);
		ObjNodeState *next(ObjNodeKeyword keyword);
	};

	class ObjNodeGeometricVertexState : public ObjNodeCategoryVertexDataState {
	public:
		ObjNodeGeometricVertexState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);

	};
	class ObjNodeTextureVertexState : public ObjNodeCategoryVertexDataState {
	public:
		ObjNodeTextureVertexState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);
	};
	class ObjNodeVertexNormalState : public ObjNodeCategoryVertexDataState {
	public:
		ObjNodeVertexNormalState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);
	};
	class ObjNodeParameterSpaceVertexState : public ObjNodeCategoryVertexDataState {
	public:
		ObjNodeParameterSpaceVertexState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
	};

	//display render attribute
	class ObjNodeCategoryDisplayRenderAttributeState : public ObjNodeState {
	public:
		ObjNodeCategoryDisplayRenderAttributeState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);
		ObjNodeState *next(ObjNodeKeyword keyword);
	};
	class ObjNodeMaterialNameState : public ObjNodeCategoryDisplayRenderAttributeState {
	public:
		ObjNodeMaterialNameState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
	};
	class ObjNodeMaterialLibraryState : public ObjNodeCategoryDisplayRenderAttributeState {
	public:
		ObjNodeMaterialLibraryState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
	};

	//grouping
	class ObjNodeCategoryGroupingState : public ObjNodeState {
	public:
		ObjNodeCategoryGroupingState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
		void update(const ObjNodeData &data, MeshModelBuilder &builder);
		ObjNodeState *next(ObjNodeKeyword keyword);
	};
	class ObjNodeGroupNameState : public ObjNodeCategoryGroupingState {
	public:
		ObjNodeGroupNameState(ObjNodeFSM *fsm);
		void parse(const std::vector<std::string> &tokens, ObjNodeData &data);
	};

}
